package com.ecoolex.chinadutyfree.retailer.core.manager;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import com.ecoolex.framework.common.util.BeanUtil;
import com.ecoolex.framework.common.util.MessageFormatter;
import com.ecoolex.union.pay.retailer.core.manager.UnionPayCouponCurrencyCodeManager;
import com.ecoolex.union.pay.retailer.core.manager.UnionPayCouponManager;
import com.ecoolex.union.pay.retailer.core.manager.UnionPayPopularMerchantManager;
import com.ecoolex.union.pay.retailer.core.util.UnionPayCouponSecureService;
import com.ecoolex.union.pay.retailer.core.util.unionpay.LogUtil;
import com.ecoolex.union.pay.retailer.entity.UnionPayCoupon;
import com.ecoolex.union.pay.retailer.entity.UnionPayPopularMerchant;
import com.ecoolex.union.pay.retailer.enums.UnionPayCouponOperateType;
import com.ecoolex.union.pay.retailer.enums.UnionPayCouponStatus;
import com.ecoolex.union.pay.retailer.pojo.UnionPayCouponOperateRequest;
import com.ecoolex.union.pay.retailer.pojo.UnionPayCouponOperateResponse;
import com.google.gson.Gson;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ecoolex.chinadutyfree.retailer.core.dao.DirectMailGoodsOrderDAO;
import com.ecoolex.chinadutyfree.retailer.core.pojo.GoodsOrderDO;
import com.ecoolex.chinadutyfree.retailer.core.pojo.GoodsOrderDetailDO;
import com.ecoolex.chinadutyfree.retailer.core.service.ProducerService;
import com.ecoolex.chinadutyfree.retailer.entity.Coupon;
import com.ecoolex.chinadutyfree.retailer.entity.CouponClass;
import com.ecoolex.chinadutyfree.retailer.entity.DeliveryManner;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoods;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrder;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrder.Column;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrderAfterSales;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrderCoupon;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrderDetail;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrderExample;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrderExample.Criteria;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrderIdentity;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrderReceiver;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsOrderSender;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsProduct;
import com.ecoolex.chinadutyfree.retailer.entity.DirectMailGoodsShoppingCart;
import com.ecoolex.chinadutyfree.retailer.entity.FreightTemplate;
import com.ecoolex.chinadutyfree.retailer.entity.FreightTemplateCharging;
import com.ecoolex.chinadutyfree.retailer.entity.FreightTemplateChargingRegion;
import com.ecoolex.chinadutyfree.retailer.entity.PaymentOrder;
import com.ecoolex.chinadutyfree.retailer.enums.CouponClassStatus;
import com.ecoolex.chinadutyfree.retailer.enums.CouponClassType;
import com.ecoolex.chinadutyfree.retailer.enums.CouponClassUseLimit;
import com.ecoolex.chinadutyfree.retailer.enums.DirectMailGoodsOrderAfterSalesRecordType;
import com.ecoolex.chinadutyfree.retailer.enums.DirectMailGoodsOrderAfterSalesStatus;
import com.ecoolex.chinadutyfree.retailer.enums.DirectMailGoodsOrderAfterSalesType;
import com.ecoolex.chinadutyfree.retailer.enums.DirectMailGoodsOrderCouponStatus;
import com.ecoolex.chinadutyfree.retailer.enums.DirectMailGoodsOrderCouponType;
import com.ecoolex.chinadutyfree.retailer.enums.DirectMailGoodsOrderStatus;
import com.ecoolex.chinadutyfree.retailer.enums.DirectMailGoodsShoppingCartStatus;
import com.ecoolex.chinadutyfree.retailer.enums.FreightTemplatePricingModel;
import com.ecoolex.chinadutyfree.retailer.enums.PaymentOrderStatus;
import com.ecoolex.chinadutyfree.retailer.pojo.DirectMailGoodsOrderChannelDTO;
import com.ecoolex.chinadutyfree.retailer.pojo.PreDirectMailGoodsOrderDTO;
import com.ecoolex.chinadutyfree.retailer.pojo.PreSubmitDirectMailGoodsOrderDTO;
import com.ecoolex.chinadutyfree.retailer.pojo.QueryDirectMailGoodsOrderDTO;
import com.ecoolex.chinadutyfree.retailer.pojo.SubmitDirectMailGoodsOrderDTO;
import com.ecoolex.framework.common.enums.ResultCode;
import com.ecoolex.framework.common.exception.BizException;
import com.ecoolex.framework.common.pojo.Page;
import com.ecoolex.framework.common.pojo.Pagination;
import com.ecoolex.framework.common.pojo.Sorting;
import com.ecoolex.framework.common.pojo.Sortings;
import com.ecoolex.framework.common.util.BizAssert;
import com.ecoolex.framework.common.util.Check;

@Service
@Slf4j
public class DirectMailGoodsOrderManager {

    @Autowired
    private DirectMailGoodsOrderDAO dao;

    @Autowired
    private DirectMailGoodsOrderDetailManager goodsOrderDetailManager;

    @Autowired
    private DirectMailGoodsProductManager directMailGoodsProductManager;

    @Autowired
    private GoodsProductManager goodsProductManager;

    @Autowired
    private PaymentOrderManager paymentOrderManager;

    @Autowired
    private DirectMailGoodsOrderAfterSalesManager afterSalesManager;

    @Autowired
    private DirectMailGoodsOrderAfterSalesRecordManager afterSalesRecordManager;

    @Autowired
    private DirectMailGoodsOrderNoManager directMailGoodsOrderNoManager;

    @Autowired
    private DirectMailGoodsOrderSenderManager orderSenderManager;

    @Autowired
    private DirectMailGoodsOrderReceiverManager orderReceiverManager;

    @Autowired
    private DirectMailGoodsShoppingCartManager shoppingCartManager;

    @Autowired
    private DirectMailGoodsManager directMailGoodsManager;

    @Autowired
    private FreightTemplateChargingRegionManager freightTemplateChargingRegionManager;

    @Autowired
    private FreightTemplateChargingManager freightTemplateChargingManager;

    @Autowired
    private FreightTemplateManager freightTemplateManager;

    @Autowired
    private DirectMailGoodsOrderIdentityManager goodsOrderIdentityManager;

    @Autowired
    private DeliveryMannerManager deliveryMannerManager;

    @Autowired
    CouponManager couponManager;

    @Autowired
    CouponClassManager couponClassManager;

    @Autowired
    DirectMailGoodsOrderCouponManager directMailGoodsOrderCouponManager;

    @Autowired
    UnionPayCouponManager unionPayCouponManager;

    @Autowired
    UnionPayCouponSecureService unionPayCouponSecureService;

    @Autowired
    Gson gson;

    @Autowired
    RestTemplateBuilder restTemplateBuilder;

    @Autowired
    DirectMailGoodsOrderChannelManager directMailGoodsOrderChannelManager;

    @Autowired
    UnionPayPopularMerchantManager unionPayPopularMerchantManager;

    @Autowired
    DirectMailGoodsProductBuyLimitManager directMailGoodsProductBuyLimitManager;

    @Autowired
    private ProducerService producerService;

    /**
     * 根据Id查询订单，银联支付业务调用，请勿修改
     * @param orderId
     * @return
     */
    public DirectMailGoodsOrder getByOrderId(Integer orderId) {
		return dao.selectByPrimaryKeyWithLogicalDelete(orderId, DirectMailGoodsOrder.NOT_DELETED);
	}

    /**
     * 根据订单号查询，银联支付业务调用，请勿修改
     * @param orderNo
     * @return
     */
    public DirectMailGoodsOrder getByOrderNo(String orderNo) {
    	DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
    	Criteria criteria = example.createCriteria();
    	criteria.andOrderNoEqualTo(orderNo).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
    	return dao.selectOneByExample(example);
    }

    /**
     * 支付成功后更新订单
     * @param paymentOrder
     * @param updateUser
     */
    public void paySuccess(PaymentOrder paymentOrder,String updateUser) {
    	BizAssert.isTrue(PaymentOrderStatus.PAY_SUCCEED.equal(paymentOrder.getStatus()),
            ResultCode.VERIFY.build(12),
            "支付未成功, 无须处理直邮订单");
    	DirectMailGoodsOrder order = getByOrderNo(paymentOrder.getOrderNo());
	    BizAssert.isTrue(Check.notNull(order), ResultCode.VERIFY.build(11), "直邮商品订单不存在");
	    BizAssert.isTrue(DirectMailGoodsOrderStatus.canPay(order.getStatus()),
	            ResultCode.VERIFY.build(10),
	            "订单必须在未支付时设置为已支付");

	    DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
	    example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
	            order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);

	    // 修改为支付成功状态
	    order.setPaymentNo(paymentOrder.getPaymentNo());
	    order.setPaymentTime(paymentOrder.getPaymentTime());
	    order.setStatus(DirectMailGoodsOrderStatus.PAID.real());
	    order.setUpdateUser(updateUser);
	    order.setUpdateTime(java.sql.Date.from(Instant.now()));
	    int row = dao.updateByExampleSelective(order,
	            example,
	            Column.paymentNo,
	            Column.paymentTime,
	            Column.status,
	            Column.updateUser,
	            Column.updateTime);
	    BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "设置已支付失败");

	    //核销商户优惠券
	    directMailGoodsOrderCouponManager.queryByOrderIdAndType(order.getOrderId(), DirectMailGoodsOrderCouponType.STORE_COUPON.real()).forEach(
	    	orderCoupon -> {
	    		couponManager.verifyCoupon(order.getOrderId(), orderCoupon.getCouponId(), updateUser);
	    	});
	    //核销银联优惠券
        directMailGoodsOrderCouponManager.queryByOrderIdAndType(order.getOrderId(), DirectMailGoodsOrderCouponType.UNION_PAY_COUPON.real()).forEach(
                orderUnionPayCoupon -> {
                    unionPayCouponManager.verifyUnionPayCoupon(order.getUserId(), orderUnionPayCoupon.getCouponId(), order.getOrderNo(),order.getRealPrice(),orderUnionPayCoupon.getDiscountAmount() , updateUser, UnionPayCouponCurrencyCodeManager.translateCurrencyCode(order.getCurrencyType()));
                });

	    // 增加产品销量
	    goodsOrderDetailManager.queryByOrderId(order.getOrderId()).forEach(detail -> {
	        // 增加直邮产品销量
	        directMailGoodsProductManager.addSoldNumber(detail.getDirectMailProductId(),
	                detail.getBuyNumber(),
	                updateUser);
	        // 增加产品的销售数量
	        goodsProductManager.addSoldNumber(detail.getProductId(), detail.getBuyNumber(), updateUser);
	    });
	    //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
    }

    // 用户申请退款
    @Transactional
    public DirectMailGoodsOrder applyRefund(DirectMailGoodsOrder order,String reason,String evidenceImage, String updateUser) {
        BizAssert.isTrue(DirectMailGoodsOrderStatus.canRefund(order.getStatus()),
                ResultCode.VERIFY.build(10),
                "订单必须在未发货时才能申请退款");

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
        order.setStatus(DirectMailGoodsOrderStatus.AFTER_SALES.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        // 产生退款售后单，后台查询退款售后单进行退款审核操作
        DirectMailGoodsOrderAfterSales afterSales = afterSalesManager.create(order.getOrderId(),order.getUserId(),reason ,evidenceImage,DirectMailGoodsOrderAfterSalesType.REFUND, DirectMailGoodsOrderAfterSalesStatus.WAIT_AUDIT,order.getRealPrice(), updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.APPLY_REFUND,reason,evidenceImage, updateUser);

        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(11), "申请退款失败");
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return order;
    }

    // 用户申请退货
    @Transactional
    public DirectMailGoodsOrder applyReturn(DirectMailGoodsOrder order,String reason,String evidenceImage, String updateUser) {
        BizAssert.isTrue(DirectMailGoodsOrderStatus.canReturnAndExchange(order.getStatus()),
                ResultCode.VERIFY.build(10),
                "订单必须在待收货时才能申请退货");

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
        order.setStatus(DirectMailGoodsOrderStatus.AFTER_SALES.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(Date.from(Instant.now()));
        // 产生退货售后单，后台查询退货售后单进行换货审核操作
        DirectMailGoodsOrderAfterSales afterSales = afterSalesManager.create(order.getOrderId(),order.getUserId(),reason ,
        	evidenceImage,DirectMailGoodsOrderAfterSalesType.RETURN, DirectMailGoodsOrderAfterSalesStatus.WAIT_AUDIT,
        	order.getRealPrice().subtract(order.getFreight()),updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.APPLY_RETURN,reason,evidenceImage, updateUser);

        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(11), "申请退货失败");
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return order;
    }

    // 用户申请换货
    @Transactional
    public DirectMailGoodsOrder applyExchange(DirectMailGoodsOrder order,String reason,String evidenceImage, String updateUser) {
        BizAssert.isTrue(DirectMailGoodsOrderStatus.canReturnAndExchange(order.getStatus()),
                ResultCode.VERIFY.build(10),
                "订单必须在待收货时才能申请换货");

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
        order.setStatus(DirectMailGoodsOrderStatus.AFTER_SALES.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(Date.from(Instant.now()));
        // 产生换货售后单，后台查询换货售后单进行换货审核操作
        DirectMailGoodsOrderAfterSales afterSales = afterSalesManager.create(order.getOrderId(),order.getUserId(),reason ,evidenceImage,DirectMailGoodsOrderAfterSalesType.EXCHANGE, DirectMailGoodsOrderAfterSalesStatus.WAIT_AUDIT,BigDecimal.ZERO, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.APPLY_EXCHANGE,reason,evidenceImage, updateUser);

        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(11), "申请换货失败");
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return order;
    }

    //用户撤销售后
    @Transactional
    public DirectMailGoodsOrderAfterSales cancelAfterSales(DirectMailGoodsOrderAfterSales afterSales, String updateUser) {
    	BizAssert.isTrue(DirectMailGoodsOrderAfterSalesStatus.canCancelApply(afterSales.getStatus()),
            ResultCode.VERIFY.build(10),
            "无法撤销");
    	DirectMailGoodsOrder order = getByOrderId(afterSales.getOrderId());
    	DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
        //退款单，状态置为待发货
        if(DirectMailGoodsOrderAfterSalesType.REFUND.equal(afterSales.getType())) {
        	order.setStatus(DirectMailGoodsOrderStatus.PAID.real());
        }else {
        	//退货/换货单，状态置为待收货
        	order.setStatus(DirectMailGoodsOrderStatus.WAIT_RECECEIVE.real());
        }
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        //更新售后单
        afterSales.setStatus(DirectMailGoodsOrderAfterSalesStatus.CLOSE.real());
        afterSalesManager.update(afterSales, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.CANCEL, null,null, updateUser);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "撤销失败");
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return afterSales;
	}

    /**
     * 后台主动退款
     * @param order
     * @param updateUser
     * @return
     */
    @Transactional
    public DirectMailGoodsOrder refund(DirectMailGoodsOrder order,String reason, String updateUser) {
    	//查询状态是否可以退款
        BizAssert.isTrue(Check.notNull(order) && DirectMailGoodsOrderStatus.canRefund(order.getStatus()),
                ResultCode.VERIFY.build(10),
                "订单状态无法退款");
        BizAssert.isTrue(goodsOrderDetailManager.countShippedDetailByOrderId(order.getOrderId()) == 0,
                ResultCode.VERIFY.build(11),
                "订单必须不存在物流信息时才能进行确认用户退款操作");

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);

        order.setStatus(DirectMailGoodsOrderStatus.CLOSED.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        // 创建退款售后单
        DirectMailGoodsOrderAfterSales afterSales = afterSalesManager.create(order.getOrderId(),order.getUserId(),reason,null,
        	DirectMailGoodsOrderAfterSalesType.REFUND,DirectMailGoodsOrderAfterSalesStatus.SUCCESS,order.getRealPrice(),updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.MERCHANT_REFUND,reason,null, updateUser);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "退款失败");
        //调用退款接口
        BizAssert.isTrue(paymentOrderManager.refundApply(order.getOrderNo(),afterSales.getRefundPrice(), updateUser), ResultCode.VERIFY.build(11), "发起退款失败");

        // 退还库存
        recoverStockNumber(order, updateUser);
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return order;
    }

    /**
     * 后台同意退款
     * @param afterSales
     * @param updateUser
     * @return
     */
    @Transactional
    public DirectMailGoodsOrderAfterSales confirmRefund(DirectMailGoodsOrderAfterSales afterSales, String updateUser) {
    	//查询关联退款单状态是否可以确认退款
        BizAssert.isTrue(Check.notNull(afterSales) && DirectMailGoodsOrderAfterSalesStatus.canOperationRefund(afterSales.getStatus()),
                ResultCode.VERIFY.build(10),
                "售后单必须在待商家处理时才能操作确认退款");
        DirectMailGoodsOrder order = getByOrderId(afterSales.getOrderId());
        BizAssert.isTrue(goodsOrderDetailManager.countShippedDetailByOrderId(order.getOrderId()) == 0,
                ResultCode.VERIFY.build(11),
                "订单必须不存在物流信息时才能进行确认用户退款操作");

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);

        order.setStatus(DirectMailGoodsOrderStatus.CLOSED.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        // 修改退款售后单退款状态
        afterSales.setStatus(DirectMailGoodsOrderAfterSalesStatus.SUCCESS.real());
        afterSalesManager.update(afterSales, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.AGREE_REFUND,null,null, updateUser);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "退款失败");
        //调用退款接口
        BizAssert.isTrue(paymentOrderManager.refundApply(order.getOrderNo(),afterSales.getRefundPrice(), updateUser), ResultCode.VERIFY.build(11), "发起退款失败");

        // 退还库存
        recoverStockNumber(order, updateUser);
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return afterSales;
    }

    /**
     * 后台拒绝退款
     * @param afterSales
     * @param updateUser
     * @return
     */
    @Transactional
    public DirectMailGoodsOrderAfterSales refuseRefund(DirectMailGoodsOrderAfterSales afterSales,String auditOpinion, String updateUser) {
    	//查询关联退款单状态是否可以确认退款
        BizAssert.isTrue(Check.notNull(afterSales) && DirectMailGoodsOrderAfterSalesStatus.canOperationRefund(afterSales.getStatus()),
                ResultCode.VERIFY.build(10),
                "售后单必须在待商家处理时才能拒绝退款");
        DirectMailGoodsOrder order = getByOrderId(afterSales.getOrderId());
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);

        order.setStatus(DirectMailGoodsOrderStatus.PAID.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        // 修改退款售后单退款状态
        afterSales.setStatus(DirectMailGoodsOrderAfterSalesStatus.CLOSE.real());
        afterSales.setAuditOpinion(auditOpinion);
        afterSalesManager.update(afterSales, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.REFUSE_REFUND,auditOpinion,null, updateUser);

        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "拒绝退款失败");
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return afterSales;
    }

    /**
     * 后台同意退货
     * @param afterSales
     * @param updateUser
     * @return
     * @throws BizException
     */
    @Transactional
    public DirectMailGoodsOrderAfterSales confirmReturn(DirectMailGoodsOrderAfterSales afterSales, String updateUser) throws BizException {
    	//查询关联售后单状态是否可以确认退款
        BizAssert.isTrue(Check.notNull(afterSales) && DirectMailGoodsOrderAfterSalesStatus.canOperationRefund(afterSales.getStatus()),
                ResultCode.VERIFY.build(10),
                "售后单须在待商家处理时才能操作确认退货");
        DirectMailGoodsOrder order = getByOrderId(afterSales.getOrderId());
        // 修改售后单状态
        afterSales.setStatus(DirectMailGoodsOrderAfterSalesStatus.WAIT_CUSTOMER_DELIVER.real());
        int row = afterSalesManager.update(afterSales, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.AGREE_RETURN, null,null,updateUser);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "同意退货失败");
		return afterSales;
	}

    /**
     * 后台拒绝退货
     * @param afterSales
     * @param auditOpinion
     * @param updateUser
     * @return
     * @throws BizException
     */
    @Transactional
	public DirectMailGoodsOrderAfterSales refuseReturn(DirectMailGoodsOrderAfterSales afterSales, String auditOpinion, String updateUser) throws BizException {
    	//查询关联售后单状态是否可以确认退款
        BizAssert.isTrue(Check.notNull(afterSales) && DirectMailGoodsOrderAfterSalesStatus.canOperationRefund(afterSales.getStatus()),
                ResultCode.VERIFY.build(10),
                "售后单须在待商家处理时才能操作拒绝退货");

        DirectMailGoodsOrder order = getByOrderId(afterSales.getOrderId());
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
        order.setStatus(DirectMailGoodsOrderStatus.WAIT_RECECEIVE.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example,Column.status, Column.updateUser, Column.updateTime);
        // 修改售后单状态
        afterSales.setAuditOpinion(auditOpinion);
        afterSales.setStatus(DirectMailGoodsOrderAfterSalesStatus.CLOSE.real());
        afterSalesManager.update(afterSales, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.REFUSE_RETURN,auditOpinion,null, updateUser);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "拒绝退货失败");
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return afterSales;
	}

    /**
     * 后台同意换货
     * @param afterSales
     * @param updateUser
     * @return
     * @throws BizException
     */
    @Transactional
    public DirectMailGoodsOrderAfterSales confirmExchange(DirectMailGoodsOrderAfterSales afterSales, String updateUser) throws BizException {
    	//查询关联售后单状态是否可以确认退款
        BizAssert.isTrue(Check.notNull(afterSales) && DirectMailGoodsOrderAfterSalesStatus.canOperationRefund(afterSales.getStatus()),
                ResultCode.VERIFY.build(10),
                "售后单须在待商家处理时才能操作确认换货");
        DirectMailGoodsOrder order = getByOrderId(afterSales.getOrderId());
        // 修改售后单状态
        afterSales.setStatus(DirectMailGoodsOrderAfterSalesStatus.WAIT_CUSTOMER_DELIVER.real());
        int row = afterSalesManager.update(afterSales, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.AGREE_EXCHANGE, null,null,updateUser);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "同意换货失败");
		return afterSales;
	}

    /**
     * 后台拒绝换货
     * @param afterSales
     * @param auditOpinion
     * @param updateUser
     * @return
     * @throws BizException
     */
    @Transactional
	public DirectMailGoodsOrderAfterSales refuseExchange(DirectMailGoodsOrderAfterSales afterSales, String auditOpinion, String updateUser) throws BizException {
    	//查询关联售后单状态是否可以确认退款
        BizAssert.isTrue(Check.notNull(afterSales) && DirectMailGoodsOrderAfterSalesStatus.canOperationRefund(afterSales.getStatus()),
                ResultCode.VERIFY.build(10),
                "售后单须在待商家处理时才能操作拒绝换货");

        DirectMailGoodsOrder order = getByOrderId(afterSales.getOrderId());
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
        order.setStatus(DirectMailGoodsOrderStatus.WAIT_RECECEIVE.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example,Column.status, Column.updateUser, Column.updateTime);
        // 修改售后单状态
        afterSales.setAuditOpinion(auditOpinion);
        afterSales.setStatus(DirectMailGoodsOrderAfterSalesStatus.CLOSE.real());
        afterSalesManager.update(afterSales, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.REFUSE_EXCHANGE,auditOpinion,null, updateUser);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "拒绝换货失败");
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return afterSales;
	}

	/**
	 * 商户后台确认收货
	 * @param afterSales
	 * @param updateUser
	 * @return
	 */
    @Transactional
	public DirectMailGoodsOrderAfterSales confirmReceipt(DirectMailGoodsOrderAfterSales afterSales, String updateUser) {
		//查询关联售后单状态是否可以确认收货
        BizAssert.isTrue(DirectMailGoodsOrderAfterSalesStatus.canConfirmReceipt(afterSales.getStatus()),
                ResultCode.VERIFY.build(10),
                "无法确认收货");
        DirectMailGoodsOrder order = getByOrderId(afterSales.getOrderId());
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
        //退货单，直接退款
        if(DirectMailGoodsOrderAfterSalesType.RETURN.equal(afterSales.getType())) {
        	order.setStatus(DirectMailGoodsOrderStatus.CLOSED.real());
        	// 调用退款接口
            BizAssert.isTrue(paymentOrderManager.refundApply(order.getOrderNo(),afterSales.getRefundPrice(), updateUser), ResultCode.VERIFY.build(11), "发起退款失败");
            // 退还库存
            recoverStockNumber(order, updateUser);
        //换货单
        }else if(DirectMailGoodsOrderAfterSalesType.EXCHANGE.equal(afterSales.getType())) {
        	order.setStatus(DirectMailGoodsOrderStatus.PAID.real());
        	//清除所有订单明细中的发货记录Id
        	goodsOrderDetailManager.cleanAllShippedRecordId(order.getOrderId(), updateUser);
        }
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example,Column.status, Column.updateUser, Column.updateTime);
        // 修改售后单状态
        afterSales.setStatus(DirectMailGoodsOrderAfterSalesStatus.SUCCESS.real());
        afterSalesManager.update(afterSales, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.CONFIRM_RECEIVED,null,null, updateUser);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "确认收货失败");

        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
		return afterSales;
	}

    /**
	 * 商户后台拒绝收货
	 * @param afterSales
	 * @param updateUser
	 * @return
	 */
    @Transactional
	public DirectMailGoodsOrderAfterSales refuseReceipt(DirectMailGoodsOrderAfterSales afterSales,String reason, String updateUser) {
		//查询关联售后单状态是否可以拒绝收货
        BizAssert.isTrue(DirectMailGoodsOrderAfterSalesStatus.canConfirmReceipt(afterSales.getStatus()),
                ResultCode.VERIFY.build(10),
                "无法拒绝收货");
        DirectMailGoodsOrder order = getByOrderId(afterSales.getOrderId());
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
        order.setStatus(DirectMailGoodsOrderStatus.WAIT_RECECEIVE.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example,Column.status, Column.updateUser, Column.updateTime);
        // 修改售后单状态
        afterSales.setStatus(DirectMailGoodsOrderAfterSalesStatus.CLOSE.real());
        afterSalesManager.update(afterSales, updateUser);
        //创建售后记录
        afterSalesRecordManager.create(order.getOrderId(), afterSales.getAfterSalesId(), DirectMailGoodsOrderAfterSalesRecordType.REFUSE_RECEIVED,reason,null, updateUser);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "拒绝收货失败");

        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
		return afterSales;
	}

    // 恢复库存数量
    private void recoverStockNumber(DirectMailGoodsOrder order, String updateUser) {
        goodsOrderDetailManager.queryByOrderId(order.getOrderId()).forEach(detail -> {
            // 恢复库存数据
            boolean succeed = directMailGoodsProductManager.subtractUsedStockNumber(detail.getDirectMailProductId(),
                    detail.getBuyNumber(),
                    updateUser);
            BizAssert.isTrue(succeed, ResultCode.BIZ.build(10), "商品[{}]恢复库存失败", detail.getName());
        });
    }

    public DirectMailGoodsOrder getPossibleBeDeletedById(Integer orderId) {
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(orderId);
        return dao.selectOneByExample(example);
    }

    public Pagination<DirectMailGoodsOrder> query(QueryDirectMailGoodsOrderDTO query, Sorting[] sorts, Page page) {
        if (Check.notNullOrTrimEmpty(query.getReceiverName())) {
            query.setReceiverName('%' + query.getReceiverName() + '%');
        }
        if (Check.notNullOrTrimEmpty(query.getReceiverPhone())) {
            query.setReceiverPhone('%' + query.getReceiverPhone() + '%');
        }
        if (Check.notNullOrTrimEmpty(query.getReceiverAddress())) {
            query.setReceiverAddress('%' + query.getReceiverAddress() + '%');
        }
        if (Check.notNullOrTrimEmpty(query.getIdentityNo())) {
            query.setIdentityNo('%' + query.getIdentityNo() + '%');
        }
        if (Check.notNullOrTrimEmpty(query.getGoodsName())) {
            query.setGoodsName('%' + query.getGoodsName() + '%');
        }
        if (Check.notNullOrTrimEmpty(query.getDeliveryNo())) {
            query.setDeliveryNo('%' + query.getDeliveryNo() + '%');
        }

        return Pagination.create(page, dao.count(query), () -> dao.query(query, Sortings.builder(sorts).toSql(), page));
    }

    void refreshShippingStatus(Integer orderId) {
        dao.refreshShippingStatus(orderId);
    }


    @Transactional
    public DirectMailGoodsOrder submit(SubmitDirectMailGoodsOrderDTO submit, DirectMailGoodsOrderIdentity identity, DirectMailGoodsOrderChannelDTO channel, String createUser) {
        // 寄件人 (可为空)
        DirectMailGoodsOrderSender sender = orderSenderManager.builder(submit.getSenderId(), submit.getUserId());
        // 收件人
        DirectMailGoodsOrderReceiver receiver
                = orderReceiverManager.builder(submit.getReceiverId(), submit.getUserId());
        BizAssert.isTrue(Check.notNull(receiver), ResultCode.VERIFY.build(21), "寄件人地址不存在");

        BizAssert.isTrue(Check.notNull(submit.getDeliveryMannerId()), ResultCode.VERIFY.build(22), "配送方式为空");
        // 计算费用
        GoodsOrderDO orderDo = computingCost(submit);
        // 验证身份信息
        goodsOrderIdentityManager.verify(orderDo.getNeedIdentityInfoType(), identity);
        // 录入订单
        DirectMailGoodsOrder order = new DirectMailGoodsOrder();
        order.setUserId(submit.getUserId());
        // 获取订单编号
        order.setOrderNo(directMailGoodsOrderNoManager.builder(createUser));
        order.setCurrencyType(orderDo.getCurrencyType());
        order.setGoodsTotalPrice(orderDo.getGoodsTotalPrice());
        order.setFreight(orderDo.getFreight());
        order.setFreightRemarks(orderDo.getFreightRemarks());
        order.setTotalPrice(orderDo.getTotalPrice());
        order.setTaxPrice(orderDo.getTaxPrice());
        order.setRealPrice(orderDo.getRealPrice());
        order.setDeliveryMannerId(submit.getDeliveryMannerId());
        order.setStatus(DirectMailGoodsOrderStatus.UNPAID.real());
        order.setDeleted(DirectMailGoodsOrder.NOT_DELETED);
        order.setCreateUser(createUser);
        order.setCreateTime(Date.from(Instant.now()));
        order.setUpdateUser(createUser);
        order.setUpdateTime(Date.from(Instant.now()));
        dao.insert(order);

        // 生成商品详情,以及处理库存
        orderDo.getDetailList().forEach(detailDo -> {
            // 记录订单详情
            DirectMailGoodsOrderDetail detail = goodsOrderDetailManager.add(order, detailDo, createUser);
            // 验证库存是否合法,并且扣除库存
            boolean succeed = directMailGoodsProductManager.addUsedStockNumber(detail.getDirectMailProductId(),
                    detail.getBuyNumber(),
                    createUser);
            BizAssert.isTrue(succeed, ResultCode.BIZ.build(20), "商品[{}]库存不足", detail.getName());
            // 删除购物车记录
            shoppingCartManager.delete(detailDo.getCart(), createUser);
        });
        // 记录寄件人信息 (可能不存在)
        if (Check.notNull(sender)) {
            orderSenderManager.add(order, sender, createUser);
        }
        // 记录收件人信息
        orderReceiverManager.add(order, receiver, createUser);
        // 添加身份信息
        goodsOrderIdentityManager.add(order, orderDo.getNeedIdentityInfoType(), identity, createUser);

        if(Check.notNull(channel)){
            //商户信息
            UnionPayPopularMerchant unionPayPopularMerchant = null;
            if(Check.notNullOrEmpty(channel.getMerchantId())){
                unionPayPopularMerchant = unionPayPopularMerchantManager.getByMerchantId(channel.getMerchantId());
            }
            //保存订单渠道信息
            directMailGoodsOrderChannelManager.save(order, channel, unionPayPopularMerchant, createUser);
        }
        //保存优惠券使用数据
        saveCouponDiscountData(order,orderDo,submit, order.getUserId(), order.getCreateUser());
        try {
            //订单推送消息
            producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        }catch (Exception ignore){
            //nothing
            log.info("订单推送消息",ignore);
        }
        return order;
    }

    /**
     * 保存订单使用优惠券的数据
     * @param order
     * @param orderDo
     * @param couponDiscount
     */
    private void saveCouponDiscountData(final DirectMailGoodsOrder order,final GoodsOrderDO orderDo, final SubmitDirectMailGoodsOrderDTO couponDiscount,final Integer userId,final String createdUser){
        if(Check.notNullAndGteZero(couponDiscount.getStoreCouponId())){
            DirectMailGoodsOrderCoupon orderCoupon = new DirectMailGoodsOrderCoupon();
            orderCoupon.setOrderId(order.getOrderId());
            orderCoupon.setUserId(order.getUserId());
            orderCoupon.setDeleted(DirectMailGoodsOrderCoupon.NOT_DELETED);
            orderCoupon.setStatus(DirectMailGoodsOrderCouponStatus.BINDING.real());
            orderCoupon.setCreateUser(order.getCreateUser());
            orderCoupon.setCreateTime(Date.from(Instant.now()));
            orderCoupon.setUpdateUser(order.getCreateUser());
            orderCoupon.setUpdateTime(Date.from(Instant.now()));
            orderCoupon.setCouponId(couponDiscount.getStoreCouponId());
            orderCoupon.setCouponType(DirectMailGoodsOrderCouponType.STORE_COUPON.real());
            orderCoupon.setDiscountAmount(orderDo.getStoreCouponDiscount());
            //商户券锁券
            couponManager.lockCoupon(order.getOrderId(),orderCoupon.getCouponId(),createdUser);
            directMailGoodsOrderCouponManager.save(orderCoupon);
        }
        if (Check.notNullAndGteZero(couponDiscount.getUnionPayCouponId())){
            DirectMailGoodsOrderCoupon orderCoupon = new DirectMailGoodsOrderCoupon();
            orderCoupon.setOrderId(order.getOrderId());
            orderCoupon.setUserId(order.getUserId());
            orderCoupon.setDeleted(DirectMailGoodsOrderCoupon.NOT_DELETED);
            orderCoupon.setStatus(DirectMailGoodsOrderCouponStatus.BINDING.real());
            orderCoupon.setCreateUser(order.getCreateUser());
            orderCoupon.setCreateTime(Date.from(Instant.now()));
            orderCoupon.setUpdateUser(order.getCreateUser());
            orderCoupon.setUpdateTime(Date.from(Instant.now()));
            orderCoupon.setCouponId(couponDiscount.getUnionPayCouponId());
            orderCoupon.setCouponType(DirectMailGoodsOrderCouponType.UNION_PAY_COUPON.real());
            orderCoupon.setDiscountAmount(orderDo.getUnionPayCouponDiscount());
            BigDecimal transPrice = orderDo.getRealPrice().add(orderDo.getUnionPayCouponDiscount().abs());
            unionPayCouponManager. lockUnionPayCoupon(order.getOrderNo(), orderCoupon.getCouponId(), transPrice, userId, createdUser, UnionPayCouponCurrencyCodeManager.translateCurrencyCode(order.getCurrencyType()));
            directMailGoodsOrderCouponManager.save(orderCoupon);
        }
    }

    /**
     * 预计算费用
     *
     * @param submit
     *
     * @return
     */
    public PreDirectMailGoodsOrderDTO preComputingCost(PreSubmitDirectMailGoodsOrderDTO submit) {
        GoodsOrderDO orderDo = computingCost(submit);
        orderDo.setStoreCouponDiscount(orderDo.getStoreCouponDiscount().abs());
        orderDo.setUnionPayCouponDiscount(orderDo.getUnionPayCouponDiscount().abs());
        return BeanUtil.copy(orderDo, new PreDirectMailGoodsOrderDTO());
    }

    /**
     * 计算费用
     *
     * @param submit
     *
     * @return
     */
    private GoodsOrderDO computingCost(SubmitDirectMailGoodsOrderDTO submit) {
        // 收件人 (可能为空, 需要计算费用)
        DirectMailGoodsOrderReceiver receiver
                = orderReceiverManager.builder(submit.getReceiverId(), submit.getUserId());
        // 购物车
        BizAssert.isTrue(Check.notNullOrEmpty(submit.getCartIdList()), ResultCode.VERIFY.build(30), "至少选择一件商品");
        // 当未输入配送方式时,验证商品是否存在相同的配送方式
        if (Check.isNull(submit.getDeliveryMannerId())) {
            List<DeliveryManner> deliveryMannerList
                    = deliveryMannerManager.queryByUserShoppingCartIds(submit.getUserId(), submit.getCartIdList());
            BizAssert.isTrue(Check.notNull(deliveryMannerList), ResultCode.VERIFY.build(36), "已选中商品没有统一的配送方式, 无法下单");
        }
        List<GoodsOrderDetailDO> detailList = submit.getCartIdList().stream().map(cartId -> {
            DirectMailGoodsShoppingCart cart = shoppingCartManager.getByIdAndUserId(cartId, submit.getUserId());
            BizAssert.isTrue(Check.notNull(cart), ResultCode.VERIFY.build(31), "购物车产品不存在");
            BizAssert.isTrue(cart.getBuyNumber() > 0 && cart.getBuyNumber() < 100,
                    ResultCode.VERIFY.build(36),
                    "商品[{}]数量必须小于100",
                    cart.getName());
            BizAssert.isTrue(DirectMailGoodsShoppingCartStatus.VALID.equal(cart.getStatus()),
                    ResultCode.VERIFY.build(32),
                    "商品[{}] 已失效",
                    cart.getName());
            //限购检查
            directMailGoodsProductBuyLimitManager.check(cart);
            // 检查物流方式是否都支持
            // if (Check.notNull(submit.getDeliveryMannerId())) {
            // BizAssert.isTrue(goodsDeliveryMannerManager.isSupport(cart.getDirectMailGoodsId(),
            // submit.getDeliveryMannerId()), ResultCode.VERIFY.build(33), "{} 不支持当前物流方式", cart.getName());
            // }

            // 直邮商品对象
            DirectMailGoods goods = directMailGoodsManager.getById(cart.getDirectMailGoodsId());
            BizAssert.isTrue(Check.notNull(goods), ResultCode.VERIFY.build(34), "直邮商品不存在");
            // 运费计算策略
            FreightTemplateChargingRegion freightTemplateChargingRegion = null;
            // 预先计算要求
            if (Check.notNull(receiver)) {
                freightTemplateChargingRegion = freightTemplateChargingRegionManager.getByFreightTemplateIdAndRegionId(
                        goods.getFreightTemplateId(),
                        receiver.getRegionId());

                BizAssert.isTrue(Check.notNull(freightTemplateChargingRegion),
                        ResultCode.VERIFY.build(35),
                        "商品[{}]不支持配送地区",
                        cart.getName());
            } /*else {
                // 还未填写收件人时, 取第一个种策略好了, 将就一下
                freightTemplateChargingRegion
                        = freightTemplateChargingRegionManager.getByFreightTemplateId(goods.getFreightTemplateId());
            }*/

            //获取产品的税率
            DirectMailGoodsProduct product = directMailGoodsProductManager.getById(cart.getDirectMailProductId());

            // 返回值
            GoodsOrderDetailDO detail = new GoodsOrderDetailDO();
            // 购物车对象
            detail.setCart(cart);
            // 直邮商品
            detail.setGoods(goods);
            // 运费模板计费模式
            detail.setFreightTemplateChargingRegion(freightTemplateChargingRegion);
            //产品税率
            detail.setTaxRate(product.getTaxRate());
            return detail;
        }).collect(Collectors.toList());

        // 检查币种是否一致
        String[] currencyTypeList
                = detailList.stream().map(detail -> detail.getCart().getCurrencyType()).distinct().toArray(String[]::new);
        BizAssert.isTrue(currencyTypeList.length == 1, ResultCode.BIZ.build(10), "币种{}, 不支持混合币种支付");

        // 计算运费, 查询商品下有多少运费模板
        StringBuffer freightRemarks = new StringBuffer();
        //默认邮费为零
        BigDecimal freight = BigDecimal.ZERO;
        if(Check.notNull(receiver)){
            freight = detailList.stream()
                    // 数据先分组
                    .collect(Collectors.groupingBy(detail -> detail.getFreightTemplateChargingRegion().getChargingId()))
                    // 处理每个计费策略
                    .entrySet().stream().map(group -> {
                        FreightTemplateCharging charging = freightTemplateChargingManager.getById(group.getKey());
                        BizAssert.isTrue(Check.notNull(charging), ResultCode.BIZ.build(11), "运费模板计费策略不存在");
                        FreightTemplate template = freightTemplateManager.getById(charging.getFreightTemplateId());
                        BizAssert.isTrue(Check.notNull(charging), ResultCode.BIZ.build(12), "运费模板不存在");

                        freightRemarks.append(MessageFormatter.arrayFormat("运费模板[{}, {}],策略[{}, {}, {}, {}, {}],参与购物车集合={},",
                                template.getFreightTemplateId(),
                                template.getPricingModel(),
                                charging.getChargingId(),
                                charging.getBasicNumber(),
                                charging.getBasicCharging(),
                                charging.getRenewNumber(),
                                charging.getRenewCharging(),
                                Arrays.toString(group.getValue().stream().map(detail -> detail.getCart().getCartId()).toArray())));

                        BigDecimal result = BigDecimal.ZERO;

                        if (FreightTemplatePricingModel.NUMBER.equal(template.getPricingModel())) {
                            // 计算数量
                            // 统计总数量
                            int totalNumber = group.getValue().stream().map(detail -> detail.getCart().getBuyNumber()).reduce(0,
                                    (a, b) -> a + b);
                            freightRemarks.append(MessageFormatter.arrayFormat("总数量: {}件,", totalNumber));
                            // 扣除首件数
                            totalNumber = totalNumber - charging.getBasicNumber();
                            result = result.add(charging.getBasicCharging());
                            // 大于0时.算续重
                            if (totalNumber > 0) {
                                int r = totalNumber % charging.getRenewNumber();
                                // 刚刚好的
                                if (r == 0) {
                                    result = result.add(charging.getRenewCharging().multiply(
                                            BigDecimal.valueOf(totalNumber / charging.getRenewNumber())));
                                } else {
                                    // 存在余数的, 需要多加一次续重
                                    result = result.add(charging.getRenewCharging().multiply(
                                            BigDecimal.valueOf((totalNumber / charging.getRenewNumber()) + 1)));
                                }
                            }
                        } else if (FreightTemplatePricingModel.WEIGHT.equal(template.getPricingModel())) {
                            // 可能不存在产品信息需要查询一波
                            group.getValue().forEach(detail -> {
                                if (Check.isNull(detail.getProduct())) {
                                    detail.setProduct(goodsProductManager.getById(detail.getCart().getProductId()));
                                }
                            });
                            // 计算总重量
                            int totalWeight = group.getValue().stream().map(
                                    detail -> detail.getProduct().getWeight() * detail.getCart().getBuyNumber()).reduce(0,
                                    (a, b) -> a + b);
                            freightRemarks.append(MessageFormatter.arrayFormat("总重量: {}k,", totalWeight));
                            // 扣除首件数
                            totalWeight = totalWeight - charging.getBasicNumber();
                            result = result.add(charging.getBasicCharging());
                            // 大于0时.算续重
                            if (totalWeight > 0) {
                                int r = totalWeight % charging.getRenewNumber();
                                // 刚刚好的
                                if (r == 0) {
                                    result = result.add(charging.getRenewCharging().multiply(
                                            BigDecimal.valueOf(totalWeight / charging.getRenewNumber())));
                                } else {
                                    // 存在余数的, 需要多加一次续重
                                    result = result.add(charging.getRenewCharging().multiply(
                                            BigDecimal.valueOf((totalWeight / charging.getRenewNumber()) + 1)));
                                }
                            }
                        } else {
                            BizAssert.throwBizException(ResultCode.BIZ.build(13), "运费模板计算模式不支持");
                        }
                        freightRemarks.append(MessageFormatter.arrayFormat("总价:{}; ", result));
                        return result;
                    }).reduce(BigDecimal.ZERO, BigDecimal::add);
        }


        // 计算商品金额（含税：如有）
        BigDecimal goodsTotalPrice = detailList.stream().map(d -> d.getCart().getTotalPrice()).reduce(BigDecimal.ZERO, BigDecimal::add);

        //计算税费
        BigDecimal taxPriceTotal = detailList.stream().map(d -> {
            if(Check.notNullAndGtZero(d.getTaxRate())){
                BigDecimal taxRate = d.getTaxRate().divide(BigDecimal.valueOf(100));
                BigDecimal taxPrice = d.getCart().getRetailPrice().multiply(taxRate).multiply(BigDecimal.valueOf(d.getCart().getBuyNumber()));
                return taxPrice;
            }else {
                return BigDecimal.ZERO;
            }

        }).reduce(BigDecimal.ZERO, BigDecimal::add);

        // 计算订单金额
        BigDecimal totalPrice = goodsTotalPrice.add(freight);

        // 录入订单
        GoodsOrderDO order = new GoodsOrderDO();

        BigDecimal subTotal = goodsTotalPrice;

        //店铺优惠
        BigDecimal storeCouponDiscount = BigDecimal.ZERO.subtract(computingStoreCouponDiscount(subTotal,submit));

        subTotal = subTotal.subtract(storeCouponDiscount);

        //银联优惠
        BigDecimal unionPayCouponDiscount = BigDecimal.ZERO.subtract(computingUnionPayCouponDiscount(subTotal, submit, currencyTypeList[0]));

        // 计算订单实付金额
        BigDecimal realPrice = totalPrice.add(storeCouponDiscount).add(unionPayCouponDiscount);

        // 获取身份信息的类型
        OptionalInt needIdentityInfoType = detailList.stream().mapToInt(detail -> detail.getGoods().getNeedIdentityInfoType()).max();

        BizAssert.isTrue(needIdentityInfoType.isPresent(), ResultCode.VERIFY.build(40), "需要的身份信息类型不存在");

        order.setCurrencyType(currencyTypeList[0]);
        order.setGoodsTotalPrice(goodsTotalPrice);
        order.setFreight(freight);
        order.setFreightRemarks(freightRemarks.toString());
        order.setTotalPrice(totalPrice);
        order.setDeliveryMannerId(submit.getDeliveryMannerId());
        order.setDetailList(detailList);
        order.setNeedIdentityInfoType(needIdentityInfoType.getAsInt());
        order.setStoreCouponDiscount(storeCouponDiscount);
        order.setUnionPayCouponDiscount(unionPayCouponDiscount);
        order.setTaxPrice(taxPriceTotal);
        order.setRealPrice(realPrice);

        return order;
    }

    /**
     * 计算店铺优惠
     * @param couponDiscount
     * @return
     */
    private BigDecimal computingStoreCouponDiscount(BigDecimal goodsTotalPrice,SubmitDirectMailGoodsOrderDTO couponDiscount){

        BigDecimal storeDiscount = BigDecimal.ZERO;
        Integer insiedCoupon = couponDiscount.getStoreCouponId();
        if(Check.notNullAndGteZero(insiedCoupon)){
            Coupon insideCoupon = couponManager.getCouponById(insiedCoupon);
            if(Check.notNull(insideCoupon) && Check.notNullAndGteZero(insideCoupon.getCouponClassId())){
                CouponClass couponClass = couponClassManager.getCouponClassById(insideCoupon.getCouponClassId());
                if(!CouponClassStatus.ONLINE.real().equals(couponClass.getStatus())){
                    throw new  BizException(ResultCode.BIZ.build(1),"优惠券无效");
                }
                if(CouponClassType.FULL_REDUCTION.equals(couponClass.getType()) ){
                    if(couponClass.getThresholdPrice().compareTo(goodsTotalPrice)==-1){
                        throw new  BizException(ResultCode.BIZ.build(2), "未达到满减限额");
                    }
                }
                if(CouponClassUseLimit.MUTEX.equals(couponClass.getUseLimit()) && Check.notNullAndGtZero(couponDiscount.getUnionPayCouponId())){
                    throw new  BizException(ResultCode.BIZ.build(3), "优惠券互斥");
                }
                storeDiscount = storeDiscount.add(couponClass.getReducePrice());
            }
        }
        return storeDiscount;
    }

    /**
     * 计算银联优惠
     *
     * @param goodsTotalPrice
     * @param couponDiscount
     * @return
     */
    private BigDecimal computingUnionPayCouponDiscount(BigDecimal goodsTotalPrice, SubmitDirectMailGoodsOrderDTO couponDiscount, String currencyType){
        BigDecimal unionPayDiscount = BigDecimal.ZERO;
        Integer userId = couponDiscount.getUserId();
        Integer unionPayCouponId = couponDiscount.getUnionPayCouponId();
        if (Check.notNullAndGteZero(unionPayCouponId)){
            UnionPayCoupon unionPayCoupon =  unionPayCouponManager.getCouponByUserId(userId,unionPayCouponId, UnionPayCouponStatus.UNUSED);
            if(Check.notNull(unionPayCoupon) && Check.eq(unionPayCoupon.getStatus(), UnionPayCouponStatus.UNUSED.real())){

                Optional<UnionPayCouponOperateResponse> queryEntity = callQueryUnionPayCoupon(goodsTotalPrice,unionPayCoupon,currencyType);
                if(queryEntity.isPresent()){
                    UnionPayCouponOperateResponse entity = queryEntity.get();
                    Instant now =  Instant.now();
                    Instant validStartDate =now;
                    if(Check.notNull(entity.getValidStartDate())){
                        try{
                            validStartDate = LocalDateTime.from(DateTimeFormatter.ofPattern("yyyyMMddHHmmss").parse(entity.getValidStartDate()+"000000")).toInstant(ZoneOffset.ofHours(8));
                        }catch (Exception e){
                            LogUtil.writeErrorLog("validStartDate",e);
                        }
                    }else {
                        validStartDate.minus(Duration.ofDays(1));
                    }
                    Instant validEndDate = now;
                    if(Check.notNull(entity.getValidStartDate())){
                        try {
                            validEndDate = LocalDateTime.from(DateTimeFormatter.ofPattern("yyyyMMddHHmmss").parse(entity.getValidEndDate()+"235959")).toInstant(ZoneOffset.ofHours(8));
                        }catch (Exception e){
                            LogUtil.writeErrorLog("validEndDate",e);
                        }
                    }else {
                        validEndDate.plus(Duration.ofDays(1));
                    }
                    unionPayCoupon.setStartEffectiveTime(Date.from(validStartDate));
                    unionPayCoupon.setEndEffectiveTime(Date.from(validEndDate));
                    unionPayCoupon.setReducePrice(entity.getDiscountAmt());
                    unionPayCouponManager.update(unionPayCoupon);
                    unionPayDiscount = unionPayDiscount.add(unionPayCoupon.getReducePrice());
                }else {
                    throw new BizException(2,"优惠券不能使用");
                }
            }
        }
        return unionPayDiscount;
    }

    /**
     * 调用银联优惠券查询接口
     * @param coupon
     * @param currencyType
     */
    private Optional<UnionPayCouponOperateResponse> callQueryUnionPayCoupon(BigDecimal totalPrice, UnionPayCoupon coupon, String currencyType){
        UnionPayCouponOperateRequest entity = new UnionPayCouponOperateRequest();
        entity.setProcType(UnionPayCouponOperateType.COUPON_QUERY.real());
        entity.setCouponId(coupon.getCouponCode());
        //保留两位小数
        entity.setTransAmt(totalPrice.setScale(2).toPlainString());
        entity.setTransCurr(UnionPayCouponCurrencyCodeManager.translateCurrencyCode(currencyType));
        return unionPayCouponSecureService.callUnionPayCouponService(entity);
    }



    // 删除订单
    @Transactional
    public void delete(DirectMailGoodsOrder order, String updateUser) {
        BizAssert.isTrue(DirectMailGoodsOrderStatus.canDelete(order.getStatus()),
                ResultCode.VERIFY.build(10),
                "非取消或者失效状态不能删除");

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);

        order.setDeleted(DirectMailGoodsOrder.IS_DELETED);
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example, Column.deleted, Column.updateUser, Column.updateTime);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "删除订单失败");

        // 删除订单详情
        goodsOrderDetailManager.deleteByOrderId(order, updateUser);
        // 删除收件人
        orderSenderManager.deleteByOrderId(order, updateUser);
        // 删除寄件人
        orderReceiverManager.deleteByOrderId(order, updateUser);
        // 删除身份认证
        goodsOrderIdentityManager.deleteByOrderId(order, updateUser);
    }

    @Transactional
    public DirectMailGoodsOrder cancel(DirectMailGoodsOrder order, String updateUser) {
        BizAssert.isTrue(DirectMailGoodsOrderStatus.canCancel(order.getStatus()),
                ResultCode.VERIFY.build(10),
                "订单必须在未支付时才能取消订单");

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);

        order.setStatus(DirectMailGoodsOrderStatus.CANCEL.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "取消订单失败");
        // 释放库存
        recoverStockNumber(order, updateUser);

        //释放商户优惠券
        directMailGoodsOrderCouponManager.queryByOrderIdAndType(order.getOrderId(), DirectMailGoodsOrderCouponType.STORE_COUPON.real()).forEach(
                orderCoupon -> {
                    couponManager.releaseCoupon(orderCoupon.getCouponId(), updateUser);
                });
        //释放银联优惠券
        directMailGoodsOrderCouponManager.queryByOrderIdAndType(order.getOrderId(), DirectMailGoodsOrderCouponType.UNION_PAY_COUPON.real()).forEach(
                orderUnionPayCoupon -> {
                    unionPayCouponManager.releaseUnionPayCoupon(order.getOrderNo(),order.getRealPrice(), orderUnionPayCoupon.getCouponId(),order.getUserId(), orderUnionPayCoupon.getDiscountAmount(), updateUser, UnionPayCouponCurrencyCodeManager.translateCurrencyCode(order.getCurrencyType()));
                });
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return order;
    }

    /**
     * 查询未支付并且过期的订单
     *
     * @param expirationTime
     * @param size
     *
     * @return
     */
    public List<DirectMailGoodsOrder> queryUnpaidAndExpiration(Date expirationTime, int size) {
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andStatusEqualTo(
                DirectMailGoodsOrderStatus.UNPAID.real()).andCreateTimeLessThanOrEqualTo(expirationTime).andDeletedEqualTo(
                DirectMailGoodsOrder.NOT_DELETED);
        example.orderBy(Column.createTime.asc());
        example.limit(size);
        return dao.selectByExample(example);
    }

    @Transactional
    public DirectMailGoodsOrder autoCancel(DirectMailGoodsOrder order, String updateUser) {
        BizAssert.isTrue(DirectMailGoodsOrderStatus.canAutoCancel(order.getStatus()),
                ResultCode.VERIFY.build(10),
                "订单必须在未支付时才能取消订单");

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);

        order.setStatus(DirectMailGoodsOrderStatus.AUTO_CANCEL.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "取消自动订单失败");
        // 释放库存
        recoverStockNumber(order, updateUser);

        //释放商户优惠券
        directMailGoodsOrderCouponManager.queryByOrderIdAndType(order.getOrderId(), DirectMailGoodsOrderCouponType.STORE_COUPON.real()).forEach(
                orderCoupon -> {
                    couponManager.releaseCoupon(orderCoupon.getCouponId(), updateUser);
                });
        //释放银联优惠券
        directMailGoodsOrderCouponManager.queryByOrderIdAndType(order.getOrderId(), DirectMailGoodsOrderCouponType.UNION_PAY_COUPON.real()).forEach(
                orderUnionPayCoupon -> {
                    unionPayCouponManager.releaseUnionPayCoupon(order.getOrderNo(),order.getRealPrice(), orderUnionPayCoupon.getCouponId(),order.getUserId(), orderUnionPayCoupon.getDiscountAmount(), updateUser, UnionPayCouponCurrencyCodeManager.translateCurrencyCode(order.getCurrencyType()));
                });
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return order;
    }

    // 发货
    @Transactional
    public DirectMailGoodsOrder shipped(DirectMailGoodsOrder order, String updateUser) {
        BizAssert.isTrue(DirectMailGoodsOrderStatus.canShipped(order.getStatus()),
                ResultCode.VERIFY.build(10),
                "订单必须在待发货才能设置发货");

        // 有部分发货状态时/查询还有多少件商品没有发货
        long notShippedDetail = goodsOrderDetailManager.countNotShippedDetailByOrderId(order.getOrderId());
        //BizAssert.isTrue(notShippedDetail == 0, ResultCode.VERIFY.build(11), "订单还有{}件商品未发货", notShippedDetail);

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId())
                .andStatusEqualTo(order.getStatus())
                .andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);

        order.setDeliveryTime(java.sql.Date.from(Instant.now()));
        /**
         * 全部发货完成才更新为已发货（待收货）
         */
        if(Check.notNull(notShippedDetail) && notShippedDetail==0){
            order.setStatus(DirectMailGoodsOrderStatus.WAIT_RECECEIVE.real());
        }
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order,
                example,
                Column.deliveryTime,
                Column.status,
                Column.updateUser,
                Column.updateTime);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "发货失败");
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return order;
    }

    /**
     * 查询已发货并且过期的订单
     *
     * @param expirationTime
     * @param size
     *
     * @return
     */
    public List<DirectMailGoodsOrder> queryShippedAndExpiration(Date expirationTime, int size) {
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andStatusEqualTo(
                DirectMailGoodsOrderStatus.WAIT_RECECEIVE.real()).andCreateTimeLessThanOrEqualTo(expirationTime).andDeletedEqualTo(
                DirectMailGoodsOrder.NOT_DELETED);
        example.orderBy(Column.createTime.asc());
        example.limit(size);
        return dao.selectByExample(example);
    }

    // 确认收货
    @Transactional
    public DirectMailGoodsOrder confirmReceipt(DirectMailGoodsOrder order, String updateUser) {
        BizAssert.isTrue(DirectMailGoodsOrderStatus.canConfirmReceipt(order.getStatus()),
                ResultCode.VERIFY.build(10),
                "订单必须待收货才能设置收货");

        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(order.getOrderId()).andStatusEqualTo(
                order.getStatus()).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);

        order.setStatus(DirectMailGoodsOrderStatus.COMPLETE.real());
        order.setUpdateUser(updateUser);
        order.setUpdateTime(java.sql.Date.from(Instant.now()));
        int row = dao.updateByExampleSelective(order, example, Column.status, Column.updateUser, Column.updateTime);
        BizAssert.isTrue(row == 1, ResultCode.BIZ.build(1), "收货失败");
        //订单推送消息
        producerService.sendPushOrderToUnionPayMessage(order.getOrderId());
        return order;
    }

    public Long countTotalNumberByStatus(Integer userId, DirectMailGoodsOrderStatus status) {
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        DirectMailGoodsOrderExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(userId).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED).andStatusEqualTo(
                status.real());
        return dao.countByExample(example);
    }

    public Long countTotalNumberByStatus(Integer userId, List<DirectMailGoodsOrderStatus> status) {
        if (status.size() == 1) {
            return countTotalNumberByStatus(userId, status.get(0));
        }
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        DirectMailGoodsOrderExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(userId).andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED).andStatusIn(
                status.stream().map(DirectMailGoodsOrderStatus::real).collect(Collectors.toList()));
        return dao.countByExample(example);
    }

    public DirectMailGoodsOrder getByIdAndUserId(Integer orderId, Integer userId) {
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(orderId).andUserIdEqualTo(userId).andDeletedEqualTo(
                DirectMailGoodsOrder.NOT_DELETED);
        return dao.selectOneByExample(example);
    }

    public DirectMailGoodsOrder getByNoAndUserId(String orderNo, Integer userId) {
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderNoEqualTo(orderNo)
                .andUserIdEqualTo(userId).andDeletedEqualTo(
                DirectMailGoodsOrder.NOT_DELETED);
        return dao.selectOneByExample(example);
    }

    public DirectMailGoodsOrder getById(Integer orderId) {
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderIdEqualTo(orderId);
        return dao.selectOneByExample(example);
    }

    public DirectMailGoodsOrder getPossibleBeDeletedByOrderNo(String orderNo) {
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        example.createCriteria().andOrderNoEqualTo(orderNo);
        return dao.selectOneByExample(example);
    }

    public Pagination<DirectMailGoodsOrder> queryUserData(Integer userId, List<String> status, Sorting[] sorts, Page page) {
        DirectMailGoodsOrderExample example = new DirectMailGoodsOrderExample();
        DirectMailGoodsOrderExample.Criteria criteria = example.createCriteria();

        criteria.andUserIdEqualTo(userId);

        if (Check.notNullOrEmpty(status)) {
            if (status.size() == 1) {
                criteria.andStatusEqualTo(status.get(0));
            } else {
                criteria.andStatusIn(status);
            }

        }

        criteria.andDeletedEqualTo(DirectMailGoodsOrder.NOT_DELETED);
        example.orderBy(Sortings.builder(sorts).verify(Column::valueOf).toSql());
        example.setOffset(page.getFirstIndex());
        example.setRows(page.getPageSize());
        return Pagination.create(page, dao.countByExample(example), () -> dao.selectByExample(example));
    }

}
